#ifndef __MQ_QUEUE_HPP__
#define __MQ_QUEUE_HPP__

#include <iostream>
#include <string>
#include <unordered_map>
#include <cassert>
#include <sstream>
#include "../mqcommon/mq_helper.hpp"
#include "../mqcommon/mq_log.hpp"
#include "../mqcommon/mq_msg.pb.h"
#include <mutex>
#include <memory>
#include <cstdbool>
#include <stdio.h>

// 特别提醒：记得加分号，不然下一条语句执行不了。

namespace mq
{
    struct msgqueue
    {
        using Ptr = std::shared_ptr<msgqueue>;
        // 消息队列名字
        std::string name;
        // 是否持久化
        bool durable;
        // 是否独占
        bool exclusive;
        // 是否自动删除
        bool auto_delete;
        // 其他参数。
        // std::unordered_map<std::string,std::string> args;
        google::protobuf::Map<std::string, std::string> args;
        msgqueue() {}
        msgqueue(const std::string& qname,
            bool qdurable,
            bool qexclusive,
            bool qauto_delete,
            // std::unordered_map<std::string,std::string> qargs
            const google::protobuf::Map<std::string, std::string>& qargs
        )
            :name(qname)
            ,durable(qdurable)
            ,exclusive(qexclusive)
            ,auto_delete(qauto_delete)
            ,args(qargs.begin(),qargs.end())
        {}
        // 内部解析str_args字符串。
        void setArgs(const std::string& str_args)
        {
            // '&'分隔符。
            // key=val&key=val&
            std::vector<std::string> vct_args;
            Helper::StrHelper::split(str_args,"&",vct_args);
            for(auto& str : vct_args)
            {
                size_t pos = str.find('=');
                std::string key = str.substr(0,pos);
                std::string val = str.substr(pos + 1);
                args[key] = val;
            }
        }
        // 将args中的内容进行序列化后，返回一个字符串。
        std::string getArgs()
        {
            std::string result;
            for(auto start = args.begin();start != args.end();++start)
            {
                result += (start->first + "=" + start->second + "&");
            }

            return result;
        }
    };
    using MqueueMap = std::unordered_map<std::string,msgqueue::Ptr>;
    // 新建表、删除表、
    class MsgqueueMapper
    {
    public:
        MsgqueueMapper(const std::string& dbfile)
            :_sql_helper(dbfile)
        {
            std::string parentPath = mq::Helper::FileHelper::parentDirctory(dbfile);
            mq::Helper::FileHelper::createDirectory(parentPath);
            assert(_sql_helper.open());
            createTable();
        }
        void createTable()
        {
            std::stringstream ss;
            ss << "create table if not exists mqueue_table(";
            ss << "name varchar(32) primary key,";
            ss << "durable int,";
            ss << "excultive int,";
            ss << "auto_delete int,";
            ss << "args varchar(128));";
            
            assert(_sql_helper.exec(ss.str(),nullptr,nullptr));

            // #define CREATE_TABLE "create table if not exists mqueue_table\
            // (\
            // name varchar(32) primary key,\
            // durable int,\
            // excultive int,\
            // auto_delete int,\
            // args varchar(128));"

            // assert(_sql_helper.exec(CREATE_TABLE,nullptr,nullptr));
        }
        void removeTable()
        {
            std::stringstream ss;
            ss << "drop table if exists mqueue_table;";

            bool ret = _sql_helper.exec(ss.str(),nullptr,nullptr);
            if(ret == false)
            {
                DLOG("删除queue_table失败");
                abort();
            }
        }
        // C++分格
        bool insert(msgqueue::Ptr &queue) 
        {
            // insert into queue_table values('queue1', true, false, false, "k1=v1&k2=v2&");
            std::stringstream sql;
            sql << "insert into mqueue_table values(";
            sql << "'" << queue->name << "', ";
            sql << queue->durable << ", ";
            sql << queue->exclusive << ", ";
            sql << queue->auto_delete << ", ";
            sql << "'" << queue->getArgs() << "');";
        
            return _sql_helper.exec(sql.str(), nullptr, nullptr);
        }
        // bool insert(msgqueue::Ptr& mqp)
        // {
        //     std::stringstream ss;
        //     ss << "insert into mqueue_table values(";
        //     ss << "\'" << mqp->name << "\'," ;
        //     ss << mqp->durable  << ",";
        //     ss << mqp->exclusive  << ",";
        //     ss << mqp->auto_delete  << ",";
        //     ss << "\'" << mqp->getArgs() << "\');";

        //     return _sql_helper.exec(ss.str(),nullptr,nullptr);

        //     // char insert_values[256];
        //     // const char* insert_formate = "insert into mqueue_table values('%s',%d,%d,%d,'%s');";
        //     // snprintf(insert_values,sizeof(insert_values),insert_formate,
        //     //     mqp->name.c_str(),\
        //     //     mqp->durable,\
        //     //     mqp->exclusive,\
        //     //     mqp->auto_delete,\
        //     //     mqp->getArgs().c_str());

        //     // return _sql_helper.exec(insert_values,nullptr,nullptr);
        // }
        void remove(const std::string& name)
        {
            std::stringstream ss;
            ss << "delete from mqueue_table where name=";
            ss << "'" << name << "';";

            _sql_helper.exec(ss.str(),nullptr,nullptr);

            return ;
        }
        MqueueMap recovery()
        {
            MqueueMap result;
            std::string sql = "select name , durable , exclusive , auto_delete ,args from mqueue_table;";
            _sql_helper.exec(sql,&MsgqueueMapper::selectCallback,&result);

            return result;
        }
    private:
        static int selectCallback(void* arg,int numcol,char** row,char** fields)
        {
            MqueueMap* result = static_cast<MqueueMap*>(arg);
            msgqueue::Ptr eqp = std::make_shared<msgqueue>();
            eqp->name = row[0];
            eqp->durable = (bool)std::stoi(row[1]);
            eqp->exclusive = (bool)std::stoi(row[2]);
            eqp->auto_delete = (bool)std::stoi(row[3]);
            if(row[4])
            {
                eqp->setArgs(row[4]);
            }
            result->insert(std::make_pair(eqp->name,eqp));

            return 0;
        }
    private:
        mq::Helper::SqliteHelper _sql_helper;
    };
    class MsgqueueManager
    {
    public:
        using Ptr = std::shared_ptr<MsgqueueManager>;
        MsgqueueManager(const std::string& dbfile)
            :_mapper(dbfile)
        {}
        bool declareMsgqueue(const std::string& qname,
            bool qdurable,
            bool qexclusive,
            bool qauto_delete,
            // std::unordered_map<std::string,std::string> qargs
            const google::protobuf::Map<std::string,std::string>& qargs
            )
        {
            std::unique_lock<std::mutex> _mutex;
            if(_msgqueues.find(qname) != _msgqueues.end())
            {
                if(qdurable = true)
                {
                    msgqueue::Ptr mqp = std::make_shared<msgqueue>(qname,qdurable,qexclusive,qauto_delete,qargs);
                    bool ret = _mapper.insert(mqp);
                }

                return true;
            }
            msgqueue::Ptr mqp = std::make_shared<msgqueue>(qname,qdurable,qexclusive,qauto_delete,qargs);
            if(mqp->durable == true)
            {
                bool ret = _mapper.insert(mqp);
                if(ret == false)
                {
                    // DLOG("队列%s持续化失败",qname.c_str());

                    return false;
                }
            }
            _msgqueues.insert(std::make_pair(mqp->name,mqp));

            return true;
        }
        bool deleteMsgqueue(const std::string& qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eqp = _msgqueues.find(qname);
            if(eqp == _msgqueues.end())
            {
                return true;
            }
            if(eqp->second->durable == true)
            {
                _mapper.remove(eqp->second->name);
            }
            _msgqueues.erase(eqp);
            
            return true;
        }
        msgqueue::Ptr selectMsgQueue(const std::string& qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto eqp = _msgqueues.find(qname);

            if(eqp == _msgqueues.end())
            {
                return nullptr;
            }

            return eqp->second;
        }
        bool exist(const std::string& name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it  = _msgqueues.find(name);
            if(it == _msgqueues.end())
            {

                return false;
            }

            return true;
        }
        size_t size()
        {
            std::unique_lock<std::mutex> lock(_mutex);
           
            return _msgqueues.size();
        }
        void clear()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.removeTable();
            _msgqueues.clear();

            return ;
        }
        MqueueMap allQueues()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            
            return _msgqueues;
        }
    private:
        std::mutex _mutex;
        MsgqueueMapper _mapper;
        std::unordered_map<std::string,msgqueue::Ptr> _msgqueues;
    };
}

#endif